import test from "ava"
import ceno from "../src"
import { createHttpTestServer } from "./helpers/create-http-test-server"

test('响应解析 - JSON', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.json({
            type: 'json',
            data: 'cake'
        })
    })

    const data = await ceno.get(server.url + '/').json()

    t.deepEqual(data, {
        type: 'json',
        data: 'cake'
    })

    await server.close()
})

test('响应解析 - JSON - 204 状态码返回空字符串', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.status(204).end()
    })

    const data = await ceno.get(server.url + '/').json()

    t.is(data, '')

    await server.close()
})

test('响应解析 - JSON - 空响应体返回空字符串', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/json').end()
    })

    const data = await ceno.get(server.url + '/').json()

    t.is(data, '')

    await server.close()
})

test('响应解析 - JSON - 自定义解析器', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/json').send('{"message":"custom"}')
    })

    const data = await ceno.get(server.url + '/', {
        parseJson: (text: string) => {
            const parsed = JSON.parse(text)
            return { ...parsed, custom: true }
        }
    }).json()

    t.deepEqual(data, {
        message: 'custom',
        custom: true
    })

    await server.close()
})

test('响应解析 - JSON - Accept 头部设置', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, 'application/json')
        response.json({ success: true })
    })

    const data = await ceno.get(server.url + '/').json()
    t.deepEqual(data, { success: true })

    await server.close()
})

test('响应解析 - Text', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('text/plain').send('cake')
    })

    const data = await ceno.get(server.url + '/').text()

    t.is(data, 'cake')

    await server.close()
})

test('响应解析 - Text - Accept 头部设置', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, 'text/*')
        response.type('text/plain').send('test')
    })

    const data = await ceno.get(server.url + '/').text()
    t.is(data, 'test')

    await server.close()
})

test('响应解析 - Blob', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/octet-stream').send('cake')
    })

    const data = await ceno.get(server.url + '/').blob()

    t.is(data.type, 'application/octet-stream;charset=utf-8')
    t.is(data.size, 4)

    await server.close()
})

test('响应解析 - Blob - Accept 头部设置', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, '*/*')
        response.type('application/octet-stream').send('test')
    })

    const data = await ceno.get(server.url + '/').blob()
    t.truthy(data instanceof Blob)

    await server.close()
})

test('响应解析 - ArrayBuffer', async (t) => {
    t.plan(5)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/octet-stream').send('cake')
    })

    const data = await ceno.get(server.url + '/').arrayBuffer()
    const uint8Array = new Uint8Array(data)

    t.is(data.byteLength, 4)
    t.is(uint8Array[0], 99)  // 'c'
    t.is(uint8Array[1], 97)  // 'a'
    t.is(uint8Array[2], 107) // 'k'
    t.is(uint8Array[3], 101) // 'e'

    await server.close()
})

test('响应解析 - ArrayBuffer - Accept 头部设置', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, '*/*')
        response.type('application/octet-stream').send('test')
    })

    const data = await ceno.get(server.url + '/').arrayBuffer()
    t.truthy(data instanceof ArrayBuffer)

    await server.close()
})

test('响应解析 - FormData', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        // 创建真正的 multipart/form-data 格式
        const boundary = '----formdata-test-boundary'
        const formData = [
            `--${boundary}`,
            'Content-Disposition: form-data; name="name"',
            '',
            'cake',
            `--${boundary}`,
            'Content-Disposition: form-data; name="type"',
            '',
            'test',
            `--${boundary}--`,
            ''
        ].join('\r\n')
        
        response.type(`multipart/form-data; boundary=${boundary}`).send(formData)
    })

    const data = await ceno.get(server.url + '/').formData()

    t.truthy(data instanceof FormData)
    t.is(data.get('name'), 'cake')
    t.is(data.get('type'), 'test')

    await server.close()
})

test('响应解析 - FormData - Accept 头部设置', async (t) => {
    t.plan(3)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, 'multipart/form-data')
        
        const boundary = '----formdata-test-boundary'
        const formData = [
            `--${boundary}`,
            'Content-Disposition: form-data; name="field"',
            '',
            'value',
            `--${boundary}--`,
            ''
        ].join('\r\n')
        
        response.type(`multipart/form-data; boundary=${boundary}`).send(formData)
    })

    const data = await ceno.get(server.url + '/').formData()
    t.truthy(data instanceof FormData)
    t.is(data.get('field'), 'value')

    await server.close()
})

test('响应解析 - 多次读取同一响应', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.json({ message: 'test' })
    })

    const response = await ceno.get(server.url + '/')
    
    // 第一次读取
    const json1 = await response.clone().json()
    // 第二次读取
    const json2 = await response.clone().json()

    t.deepEqual(json1, { message: 'test' })
    t.deepEqual(json2, { message: 'test' })

    await server.close()
})

test('响应解析 - 保留原始 Accept 头部', async (t) => {
    t.plan(2)

    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        t.is(request.headers.accept, 'custom/type')
        response.json({ success: true })
    })

    const data = await ceno.get(server.url + '/', {
        headers: {
            'Accept': 'custom/type'
        }
    }).json()
    
    t.deepEqual(data, { success: true })

    await server.close()
})

test('响应解析 - JSON 解析错误处理', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/json').send('invalid json content')
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/').json(),
        { instanceOf: SyntaxError }
    )

    await server.close()
})

test('响应解析 - 自定义 JSON 解析器错误处理', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('application/json').send('valid json')
    })

    await t.throwsAsync(
        () => ceno.get(server.url + '/', {
            parseJson: () => {
                throw new Error('Custom parser error')
            }
        }).json(),
        { message: 'Custom parser error' }
    )

    await server.close()
})

test('响应解析 - 响应装饰器功能', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.json({ message: 'test' })
    })

    const response = await ceno.get(server.url + '/', {
        parseJson: (text: string) => {
            const parsed = JSON.parse(text)
            return { ...parsed, decorated: true }
        }
    })

    // 测试响应对象的 json 方法被正确装饰
    const data = await response.json()
    t.deepEqual(data, { message: 'test', decorated: true })

    await server.close()
})

test('响应解析 - 大型响应数据', async (t) => {
    const server = await createHttpTestServer()

    const largeData = Array.from({ length: 1000 }, (_, i) => ({
        id: i,
        name: `item-${i}`,
        data: 'x'.repeat(100)
    }))

    server.get('/', (request, response) => {
        response.json(largeData)
    })

    interface LargeData {
        id: number
        name: string
        data: string
    }

    const data = await ceno.get<LargeData[]>(server.url + '/').json()

    t.is(data.length, 1000)
    t.is(data[0].id, 0)
    t.is(data[999].id, 999)

    await server.close()
})

test('响应解析 - 空字符串响应', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        response.type('text/plain').send('')
    })

    const data = await ceno.get(server.url + '/').text()

    t.is(data, '')

    await server.close()
})

test('响应解析 - 二进制数据', async (t) => {
    const server = await createHttpTestServer()

    server.get('/', (request, response) => {
        const binaryData = Buffer.from([0x00, 0x01, 0x02, 0x03, 0xFF])
        response.type('application/octet-stream').send(binaryData)
    })

    const arrayBuffer = await ceno.get(server.url + '/').arrayBuffer()
    const uint8Array = new Uint8Array(arrayBuffer)

    t.is(uint8Array.length, 5)
    t.is(uint8Array[0], 0x00)
    t.is(uint8Array[1], 0x01)
    t.is(uint8Array[4], 0xFF)

    await server.close()
})